/**!
 *
 * MinDB
 *
 * Database on JavaScript
 *
 *  Copyright (c) 2012-2013 Will Wen Gunn(willwengunn@gmail.com)
 *  All rights reserved.
 *
 *  MIT License
 *
 *  Permission is hereby granted, free of charge, to any person obtaining
 *  a copy of this software and associated documentation files (the
 *  "Software"), to deal in the Software without restriction, including
 *  without limitation the rights to use, copy, modify, merge, publish,
 *  distribute, sublicense, and/or sell copies of the Software, and to
 *  permit persons to whom the Software is furnished to do so, subject to
 *  the following conditions:
 *
 *  The above copyright notice and this permission notice shall be
 *  included in all copies or substantial portions of the Software.
 *
 */

// Shims
(function(window, document) {

    if (window && document) {
        function createScript() {
            return document.createElement("script");
        }
        // JSON
        var s = document.getElementsByTagName("script")[0];
        var head = s.parentNode;

        if (!window.JSON) {
            var jsonSrc = createScript();
            jsonSrc.src = "//cdn.staticfile.org/json2/20121008/json2.min.js";
            head.insertBefore(jsonSrc, s);
        }

        if (!Function.prototype.bind || !Array.isArray) {
            // ECMAScript 5
            var shimSrc = createScript();
            shimSrc.src = "//cdn.staticfile.org/es5-shim/2.1.0/es5-shim.min.js";
            head.insertBefore(shimSrc, s);
            var shamSrc = createScript();
            shamSrc.src = "//cdn.staticfile.org/es5-shim/2.1.0/es5-sham.min.js";
            head.insertBefore(shamSrc, s);
        }

        if (!Object.create) {
            Object.create = (function() {
                function F() {}

                return function(superCtor, ctor) {
                    F.prototype = {};
                    for (var key in superCtor) {
                        F.prototype[key] = superCtor[key];
                    }
                    for (var key in ctor) {
                        F.prototype[key] = ctor[key];
                    }
                    return new F();
                }
            })();
        }
    }

})((typeof(window) !== 'undefined' ? window : this), (typeof(document) !== 'undefined' ? document : null));

// MinDB
!(function(name, def) {
    var hasDefine  = 'undefined' !== typeof define;
    var hasExports = 'undefined' !== typeof exports;

    if (hasDefine) {
        // CommonJS: SeaJS, RequireJS etc.
        define(name, [], def);
    } else if (hasExports) {
        // Node.js Module
        exports = def(require, exports, module);
    } else {
        // Normal
        this[name] = def();
    }
})('min', function(require, exports, module, undefined) {
    // Strict Mode Enabled
    'use strict';

    // MinDB
    var min = exports || {};

    // Utils
    var utils = {
        noop: function() {
            return false;
        },
        // Class Inherits
        inherits: function (ctor, superCtor) {
            ctor.super_ = superCtor;
            ctor.prototype = Object.create(superCtor.prototype, {
                constructor: {
                    value: ctor,
                    enumerable: false,
                    writable: true,
                    configurable: true
                }
            });
        },
        // Object Extend
        extend: function() {
            var target = arguments[0];

            var objs = [].slice.call(arguments, 1);

            for (var i = 0, l = objs.length; i < l; i++) {
                for (var key in objs[i]) {
                    target[key] = objs[i][key];
                }
            }

            return target;
        },
        isNumber: function(obj) {
            return toString.call(obj) == '[object Number]';
        },
        isUndefined: function(val) {
            return val === void 0;
        },
        arrayUnique: function(array) {
            var u = {};
            var ret = [];
            for (var i = 0, l = array.length; i < l; ++i) {
                if(u.hasOwnProperty(array[i])) {
                    continue;
                }
                ret.push(array[i]);
                u[array[i]] = 1;
            }
            return ret;
        },
        arrayInter: function(array) {
            var rest = [].slice.call(arguments, 1);
            return utils.arrayUnique(array).filter(function(item) {
                var ret = true;

                for (var i = 0; i < rest.length; i++) {
                    (function(index) {
                        var other = rest[index];

                        if (other.indexOf(item) < 0) {
                            ret = false;
                        }
                    })(i);
                }

                return ret;
            });
        },
        arrayDiff: function(array) {
            var rest = [].slice.call(arguments, 1);
            return array.filter(function(item) {
                var ret = true;

                for (var i = 0; i < rest.length; i++) {
                    (function(index) {
                        var other = rest[index];

                        if (other.indexOf(item) >= 0) {
                            ret = false;
                        }
                    })(i);
                }

                return ret;
            });
        }
    };

    // Navite Store Interfaces
    function memStore () {}
    memStore.prototype.get = function(key) {
        if (sessionStorage) {
            return sessionStorage.getItem(key);
        } else {
            return false;
        }
    };
    memStore.prototype.set = function(key, value) {
        if (sessionStorage) {
            return sessionStorage.setItem(key, value);
        } else {
            return false;
        }
    };
    memStore.prototype.remove = function(key) {
        if (sessionStorage) {
            return sessionStorage.removeItem(key);
        } else {
            return false;
        }
    };

    function localStore () {}
    localStore.prototype.get = function(key) {
        if (localStorage) {
            return localStorage.getItem(key);
        } else {
            return false;
        }
    };
    localStore.prototype.set = function(key, value) {
        if (localStorage) {
            return localStorage.setItem(key, value);
        } else {
            return false;
        }
    };
    localStore.prototype.remove = function(key) {
        if (localStorage) {
            return localStorage.removeItem(key);
        } else {
            return false;
        }
    };

    min.memStore = memStore;
    min.localStore = localStore;

    // EventEmitter(without `domain` module) From Node.js
    function EventEmitter() {
        this._events = this._events || {};
        this._maxListeners = this._maxListeners || defaultMaxListeners;
    }
    var defaultMaxListeners = 10;
    EventEmitter.prototype.setMaxListeners = function(n) {
        if (typeof n !== 'number' || n < 0)
            throw TypeError('n must be a positive number');
        this._maxListeners = n;
    };
    EventEmitter.prototype.emit = function(type) {
        var er, handler, len, args, i, listeners;

        if (!this._events)
            this._events = {};

        // If there is no 'error' event listener then throw.
        if (type === 'error') {
            if (!this._events.error ||
                (typeof this._events.error === 'object' &&
                    !this._events.error.length)) {
                er = arguments[1];
                if (this.domain) {
                    if (!er) er = new TypeError('Uncaught, unspecified "error" event.');
                } else if (er instanceof Error) {
                    throw er; // Unhandled 'error' event
                } else {
                    throw TypeError('Uncaught, unspecified "error" event.');
                }
                return false;
            }
        }

        handler = this._events[type];

        if (typeof handler === 'undefined')
            return false;

        if (typeof handler === 'function') {
            switch (arguments.length) {
                // fast cases
                case 1:
                    handler.call(this);
                    break;
                case 2:
                    handler.call(this, arguments[1]);
                    break;
                case 3:
                    handler.call(this, arguments[1], arguments[2]);
                    break;
                // slower
                default:
                    len = arguments.length;
                    args = new Array(len - 1);
                    for (i = 1; i < len; i++)
                        args[i - 1] = arguments[i];
                    handler.apply(this, args);
            }
        } else if (typeof handler === 'object') {
            len = arguments.length;
            args = new Array(len - 1);
            for (i = 1; i < len; i++)
                args[i - 1] = arguments[i];

            listeners = handler.slice();
            len = listeners.length;
            for (i = 0; i < len; i++)
                listeners[i].apply(this, args);
        }

        return true;
    };
    EventEmitter.prototype.addListener = function(type, listener) {
        var m;

        if (typeof listener !== 'function')
            throw TypeError('listener must be a function');

        if (!this._events)
            this._events = {};

        // To avoid recursion in the case that type === "newListener"! Before
        // adding it to the listeners, first emit "newListener".
        if (this._events.newListener)
            this.emit('newListener', type, typeof listener.listener === 'function' ?
                listener.listener : listener);

        if (!this._events[type])
        // Optimize the case of one listener. Don't need the extra array object.
            this._events[type] = listener;
        else if (typeof this._events[type] === 'object')
        // If we've already got an array, just append.
            this._events[type].push(listener);
        else
        // Adding the second element, need to change to array.
            this._events[type] = [this._events[type], listener];

        // Check for listener leak
        if (typeof this._events[type] === 'object' && !this._events[type].warned) {
            m = this._maxListeners;
            if (m && m > 0 && this._events[type].length > m) {
                this._events[type].warned = true;
                console.error('(node) warning: possible EventEmitter memory ' +
                    'leak detected. %d listeners added. ' +
                    'Use emitter.setMaxListeners() to increase limit.',
                    this._events[type].length);
                console.trace();
            }
        }

        return this;
    };
    EventEmitter.prototype.on = EventEmitter.prototype.addListener;
    EventEmitter.prototype.once = function(type, listener) {
        if (typeof listener !== 'function')
            throw TypeError('listener must be a function');

        function g() {
            this.removeListener(type, g);
            listener.apply(this, arguments);
        }

        g.listener = listener;
        this.on(type, g);

        return this;
    };
    // emits a 'removeListener' event iff the listener was removed
    EventEmitter.prototype.removeListener = function(type, listener) {
        var list, position, length, i;

        if (typeof listener !== 'function')
            throw TypeError('listener must be a function');

        if (!this._events || !this._events[type])
            return this;

        list = this._events[type];
        length = list.length;
        position = -1;

        if (list === listener ||
            (typeof list.listener === 'function' && list.listener === listener)) {
            this._events[type] = undefined;
            if (this._events.removeListener)
                this.emit('removeListener', type, listener);

        } else if (typeof list === 'object') {
            for (i = length; i-- > 0;) {
                if (list[i] === listener ||
                    (list[i].listener && list[i].listener === listener)) {
                    position = i;
                    break;
                }
            }

            if (position < 0)
                return this;

            if (list.length === 1) {
                list.length = 0;
                this._events[type] = undefined;
            } else {
                list.splice(position, 1);
            }

            if (this._events.removeListener)
                this.emit('removeListener', type, listener);
        }

        return this;
    };
    EventEmitter.prototype.removeAllListeners = function(type) {
        var key, listeners;

        if (!this._events)
            return this;

        // not listening for removeListener, no need to emit
        if (!this._events.removeListener) {
            if (arguments.length === 0)
                this._events = {};
            else if (this._events[type])
                this._events[type] = undefined;
            return this;
        }

        // emit removeListener for all listeners on all events
        if (arguments.length === 0) {
            for (key in this._events) {
                if (key === 'removeListener') continue;
                this.removeAllListeners(key);
            }
            this.removeAllListeners('removeListener');
            this._events = {};
            return this;
        }

        listeners = this._events[type];

        if (typeof listeners === 'function') {
            this.removeListener(type, listeners);
        } else {
            // LIFO order
            while (listeners.length)
                this.removeListener(type, listeners[listeners.length - 1]);
        }
        this._events[type] = undefined;

        return this;
    };
    EventEmitter.prototype.listeners = function(type) {
        var ret;
        if (!this._events || !this._events[type])
            ret = [];
        else if (typeof this._events[type] === 'function')
            ret = [this._events[type]];
        else
            ret = this._events[type].slice();
        return ret;
    };
    EventEmitter.listenerCount = function(emitter, type) {
        var ret;
        if (!emitter._events || !emitter._events[type])
            ret = 0;
        else if (typeof emitter._events[type] === 'function')
            ret = 1;
        else
            ret = emitter._events[type].length;
        return ret;
    };
    min.EventEmitter = EventEmitter;
    utils.extend(min, new EventEmitter());

    function Promise(done) {
        this.results = null;
        this.errors  = null;
        this.ended   = false;

        // Done Callback
        if ('function' === typeof done) {
            this.then(done);
        }
    }
    utils.inherits(Promise, EventEmitter);
    Promise.prototype.resolve = function() {
        // Arguments processing
        var args = slice(arguments);

        // Done
        this.emit('fulfill', args);
        this.ended   = true;
        this.results = args;

        return this;
    };
    Promise.prototype.reject = function() {
        // Arguments processing
        var args = slice(arguments);

        // Error!
        this.emit('reject', args);
        this.ended  = true;
        this.errors = args;

        return this;
    };
    Promise.prototype.then = function(onResolve, onReject) {
        var promise = new Promise();
        var self = this;

        onResolve = onResolve || utils.noop;
        onReject  = onReject  || utils.noop;

        var handle = function(ret) {
            if (!!ret && typeof ret.then === 'function') {
                ret
                    .then(function() {
                        promise.resolve.apply(promise, arguments);
                    }, function(err) {
                        promise.reject(err);
                    });
            } else if (ret instanceof Error) {
                promise.reject(ret);
            }
        };

        if (self.ended && (self.results || self.errors)) {
            if (self.results) {
                handle(onResolve.apply(null, self.results));
            }

            if (self.errors) {
                onReject.apply(null, self.errors)
                handle.apply(null, self.errors);
            }
        } else {
            self
                .once('fulfill', function(args) {
                    return handle(onResolve.apply(null, args));
                })
                .once('reject', function(err) {
                    onReject.call(null, err)
                    return handle(err);
                });
        }

        return promise;
    };
    Promise.prototype.fail = function(callback) {
        var self = this;

        if (self.ended && self.errors !== null) {
            // Reject Before
            callback.apply(self, self.errors);
        } else {
            // Event listening
            self.once('reject', function(args) {
                callback.apply(self, args);

                self.ended = false;
            });
        }

        return self;
    };
    function slice(argv) {
        var args = [];

        for (var i = 0; i < argv.length; i++) {
            args[i] = argv[i];
        }

        return args;
    }
    min.Promise = Promise;

    // Default Store
    min.store = new min.localStore();

    // Default variables
    var _keys  = {};
    var _keysTimer = null;
    var _types = {
        0 : 'mix',
        1 : 'hash',
        2 : 'list',
        3 : 'set',
        4 : 'zset'  // Sorted Set
    };

    /**
     * Fork a new MinDB object
     * @return {Object} new min object
     */
    min.fork = function() {
        var rtn = {};
        var self = this;

        for (var prop in this) {
            if (this.hasOwnProperty(prop)) {
                rtn[prop] = this[prop];
            }
        }

        return rtn;
    };

    /*********
     ** Keys **
     *********/

    /**
     * Delete a key
     * @param  {String}   key      Key
     * @param  {Function} callback Callback
     * @return {Promise}           Promise Object
     */
    min.del = function(key, callback) {
        var self = this;

        // Promise Object
        var promise = new Promise(function() {
            self.emit('del', key);
            if (_keysTimer) {
                clearTimeout(_keysTimer);
            }

            _keysTimer = setTimeout(self.save.bind(self), 5 * 1000);
        });

        // Store
        var store = this.store;

        // Callback and Promise's shim
        if ('undefined' == typeof callback) {
            callback = utils.noop;
        }

        // Key prefix
        var $key = 'min-' + key;

        if (store.async) {
            // Async Store Operating

            // Value processing
            store.remove($key, function(err) {
                if (err) {
                    // Error!
                    promise.reject(err);
                    return callback(err);
                }

                delete _keys[key];

                // Done
                promise.resolve(key);
                callback(null, key);
            });
        } else {
            try {
                store.remove($key);

                delete _keys[key];

                // Done
                promise.resolve(key);
                callback(null, key);
            } catch(err) {
                // Error!
                promise.reject(err);
                callback(err);
            }
        }

        // Event emitting
        this.emit('del', key);

        return promise;
    };

    /**
     * Check a key is exists or not
     * @param  {String}   key      Key
     * @param  {Function} callback Callback
     * @return {Promise}           Promise Object
     */
    min.exists = function(key, callback) {
        // Promise Object
        var promise = new Promise();

        callback = callback || utils.noop;

        try {
            this.get(key, function(err, value) {
                if (err) {
                    promise.resolve(false);
                    callback(null, false);
                }

                if ('undefined' == typeof value) {
                    promise.resolve(false);
                    return callback(null, false);
                } else {
                    promise.resolve(true);
                    return callback(null, true);
                }
            });
        } catch(err) {
            promise.reject(err);
            return callback(err);
        }

        return promise;
    };

    /**
     * Rename a old key
     * @param  {String}   key      the old key
     * @param  {String}   newKey   the new key
     * @param  {Function} callback Callback
     * @return {Promise}           Promise Object
     */
    min.renamenx = function(key, newKey, callback) {
        var self = this;

        // Promise object
        var promise = new Promise(function() {
            if (_keysTimer) {
                clearTimeout(_keysTimer);
            }

            _keysTimer = setTimeout(self.save.bind(self), 5 * 1000);
        });

        // Callback and Promise's shim
        if ('undefined' == typeof callback) {
            callback = utils.noop;
        }

        try {
            // Error handle
            var reject = function(err) {
                promise.reject(err);
                callback(err);
            };

            var type = null;
            var value = null;

            min.exists(key)
                .then(function(exists) {
                    if (!exists) {
                        var err = new Error('no such key');

                        reject(err);
                    } else {
                        return min.get(key);
                    }
                })
                .then(function(_value) {
                    type = _keys[key];
                    value = _value;

                    return min.del(key);
                })
                .then(function() {
                    return min.set(newKey, value, callback);
                })
                .then(function() {
                    _keys[newKey] = type;
                    promise.resolve('OK');
                    callback(null, 'OK');
                })
                .fail(reject);

        } catch(err) {
            reject(err);
        }

        return promise;
    };

    /**
     * Rename a old key when the old key is not equal to the new key
     * and the old key is exiest.
     * @param  {String}   key      the old key
     * @param  {String}   newKey   the new key
     * @param  {Function} callback Callback
     * @return {Promise}           Promise Object
     */
    min.rename = function(key, newKey, callback) {
        var self = this;
        // Promise object
        var promise = new Promise(function() {
            if (_keysTimer) {
                clearTimeout(_keysTimer);
            }

            _keysTimer = setTimeout(self.save.bind(self), 5 * 1000);
        });

        // Error handle
        var reject = function(err) {
            promise.reject(err);
            callback(err);
        };

        // Callback and Promise's shim
        if ('undefined' == typeof callback) {
            callback = utils.noop;
        }

        if (key == newKey) {
            // The origin key is equal to the new key
            reject(new Error('The key is equal to the new key.'));
        } else {
            self.renamenx.apply(self, arguments)
                .then(promise.resolve.bind(promise))
                .fail(promise.reject.bind(promise));
        }
        return promise;
    };

    /**
     * Return the keys which match by the pattern
     * @param  {String}   pattern  Pattern
     * @param  {Function} callback Callback
     * @return {Promise}           Promise Object
     */
    min.keys = function(pattern, callback) {

        // Promise object
        var promise = new Promise();

        // Stored keys
        var keys = Object.keys(_keys);

        // Callback and Promise's shim
        callback = callback || utils.noop;

        // Filter
        var filter = pattern
            .replace('?', '(.)')
            .replace('*', '(.*)');
        filter = new RegExp(filter);

        var ret = [];

        for (var i = 0; i < keys.length; i++) {
            if (keys[i].match(filter)) {
                ret.push(keys[i]);
            }
        }

        // Done
        promise.resolve(ret);
        callback(null, ret);

        return promise;
    };

    /**
     * Return a key randomly
     * @param  {Function} callback Callback
     * @return {Promise}           Promise Object
     */
    min.randomkey = function(callback) {

        // Promise Object
        var promise = new Promise();

        // Stored keys
        var keys = Object.keys(_keys);

        // Callback and Promise's shim
        if ('undefined' == typeof callback) {
            callback = utils.noop;
        }

        // Random Key
        var index = Math.round(Math.random() * (keys.length - 1));

        // Done
        var $key = keys[index];
        promise.resolve($key);
        callback(null, $key);

        return promise;
    };

    /**
     * Return the value's type of the key
     * @param  {String}   key      the key
     * @param  {Function} callback Callback
     * @return {Promise}           Promise Object
     */
    min.type = function(key, callback) {

        // Promise Object
        var promise = new Promise();

        // Callback and Promise's shim
        callback = callback || utils.noop;

        if (_keys.hasOwnProperty(key)) {
            promise.resolve(_types[_keys[key]]);
            callback(null, callback);
        } else {
            promise.resolve(null);
            callback(null, null);
        }

        return promise;
    };

    /**
     * Remove all keys in the db
     * @param  {Function} callback Callback
     * @return {Object}            min
     */
    min.empty = function(callback) {
        var self = this;
        var promise = new Promise(function(len) {
            self.emit('empty', len);
            if (_keysTimer) {
                clearTimeout(_keysTimer);
            }

            _keysTimer = setTimeout(self.save.bind(self), 5 * 1000);
        });
        var keys = Object.keys(_keys);
        var last = null;
        var removeds = 0;
        callback = callback || utils.noop;

        (function loop(key) {
            if (key) {
                self.del(key, function(err) {
                    if (!err) {
                        removeds++;
                    }

                    loop(keys.shift());
                });
            } else {
                promise.resolve(removeds);
                callback(null, removeds);
            }
        })(keys.shift());

        return promise;
    };

    /**
     * Save the dataset to the Store Interface manually
     * @param  {Function} callback callback
     * @return {Promise}           promise
     */
    min.save = function(callback) {
        var self = this;
        var promise = new Promise(function(dump, strResult) {
            self.emit('save', dump, strResult);
        });
        callback = callback || utils.noop;

        self.set('min_keys', JSON.stringify(_keys))
            .then(function() {
                return self.dump();
            })
            .then(function(dump, strResult) {
                promise.resolve(dump, strResult);
                callback(dump, strResult);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    /**
     * Return the dataset of MinDB
     * @param  {Function} callback callback
     * @return {Promise}           promise
     */
    min.dump = function(callback) {
        var self = this;
        var promise = new Promise();
        callback = callback || utils.noop;

        var rtn = {};

        self.keys('*')
            .then(function(keys) {
                (function loop(key) {
                    if (key) {
                        self.get(key)
                            .then(function(value) {
                                rtn[key] = value;
                                loop(keys.shift());
                            })
                            .fail(function(err) {
                                promise.reject(err);
                                callback(err);
                            });
                    } else {
                        var strResult = JSON.stringify(rtn);
                        promise.resolve(rtn, strResult);
                        callback(null, rtn, strResult);
                    }
                })(keys.shift());
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    /**
     * Restore the dataset to MinDB
     * @param  {Object}   dump     dump object
     * @param  {Function} callback callback
     * @return {Promise}           promise
     */
    min.restore = function(dump, callback) {
        var self = this;
        var promise = new Promise(function() {
            self.save(function() {
                self.emit('restore');
            });
        });
        callback = callback || utils.noop;

        var keys = Object.keys(dump);

        (function loop(key, done) {
            if (key) {
                self.set(key, dump[key])
                    .then(function() {
                        loop(keys.shift(), done);
                    })
                    .fail(function(err) {
                        promise.reject(err);
                        callback(err);
                    });
            } else {
                done();
            }
        })(keys.shift(), function() {
            self
                .exists('min_keys')
                .then(function(exists) {
                    if (exists) {
                        return self.get('min_keys');
                    } else {
                        promise.resolve();
                        callback();
                    }
                })
                .then(function(keys) {
                    _keys = JSON.parse(keys);

                    promise.resolve();
                    callback();
                });
        });

        return promise;
    };

    /******************************
     ** Mix(String/Number/Object) **
     ******************************/

    /**
     * Set the value of a key
     * @param  {String}   key      Key
     * @param  {Mix}      value    Value
     * @param  {Function} callback Callback
     * @return {Promise}           Promise Object
     */
    min.set = function(key, value, callback) {
        var self = this;

        // Promise Object
        var promise = new Promise(function() {
            if (_keysTimer) {
                clearTimeout(_keysTimer);
            }

            _keysTimer = setTimeout(self.save.bind(self), 5 * 1000);
        });

        // Store
        var store = this.store;

        // Callback and Promise's shim
        callback = callback || utils.noop;

        // Key prefix
        var $key = 'min-' + key;

        if (store.async) {
            // Async Store Operating

            // Value processing
            var $value = JSON.stringify(value);
            store.set($key, $value, function(err) {
                if (err) {
                    // Error!
                    promise.reject(err);
                    return callback(err);
                }

                _keys[key] = 0;

                // Done
                promise.resolve(key, value);
                callback(null, key, value);
            });
        } else {
            try {
                // Value processing
                var $value = JSON.stringify(value);
                store.set($key, $value);
                _keys[key] = 0;

                // Done
                promise.resolve(key, value);
                callback(null, key, value);
            } catch(err) {
                // Error!
                promise.reject(err);
                callback(err);
            }
        }

        // Event emitting
        this.emit('set', key, value);

        return promise;
    };

    /**
     * Set the value of a key, only if the key does not exist
     * @param  {String}   key      the key
     * @param  {Mix}      value    Value
     * @param  {Function} callback Callback
     * @return {Promise}           Promise Object
     */
    min.setnx = function(key, value, callback) {

        // Promise Object
        var promise = new Promise();

        var self = this;

        // Callback and Promise's shim
        callback = callback || utils.noop;

        self.exists(key, function(err, exists) {
            if (err) {
                callback(err);
                promise.reject(err);
            }

            if (exists) {
                // The key is exists
                return promise.reject(new Error('The key is exists.'));
            } else {
                self.set(key, value, callback)
                    .then(function(key, value) {
                        // Done
                        callback(null, key, value);
                        promise.resolve(key, value);
                    })
                    .fail(function(err) {
                        callback(err);
                        promise.reject(err);
                    });
            }
        });

        return promise;
    };

    /**
     * Set the value and expiration of a key
     * @param  {String}   key      key
     * @param  {Number}   seconds  TTL
     * @param  {Mix}      value    value
     * @param  {Function} callback Callback
     * @return {Promise}           promise object
     */
    min.setex = function(key, seconds, value, callback) {

        // Promise Object
        var promise = new Promise();

        var self = this;

        // TTL
        function timeout() {
            self.del(key, utils.noop);
        }

        // Callback and Promise's shim
        callback = callback || utils.noop;

        // Set
        self.set(key, value, function(err, result) {
            // Done
            setTimeout(timeout, seconds * 1000);
            callback(err, result);
        })
            .then(function(key, value) {
                // Done
                setTimeout(timeout, seconds * 1000);
                promise.resolve(key, value);
                callback(null, key, value);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    /**
     * Set the value and expiration in milliseconds of a key
     * @param  {String}   key      key
     * @param  {Number}   millionseconds  TTL
     * @param  {Mix}      value    value
     * @param  {Function} callback Callback
     * @return {Promise}           promise object
     */
    min.psetex = function(key, milliseconds, value, callback) {

        // Promise Object
        var promise = new Promise();

        var self = this;

        // TTL
        function timeout() {
            self.del(key, utils.noop);
        }

        // Callback and Promise's shim
        callback = callback || utils.noop;

        // Set
        self.set(key, value, function(err, result) {
            // Done
            setTimeout(timeout, milliseconds);
            callback(err, result);
        })
            .then(function() {
                // Done
                setTimeout(timeout, milliseconds);
                promise.resolve.apply(promise, arguments);
            })
            .fail(promise.reject.bind(promise));

        return promise;
    };

    /**
     * Set multiple keys to multiple values
     * @param  {Object}   plainObject      Object to set
     * @param  {Function} callback Callback
     * @return {Promise}           promise object
     */
    min.mset = function(plainObject, callback) {
        var promise = new Promise();

        var self = this;

        // keys
        var keys = Object.keys(plainObject);
        // counter
        var i = 0;

        // the results and errors to return
        var results = [];
        var errors = [];

        // Callback and Promise's shim
        callback = callback || utils.noop;

        // Loop
        function next(key, index) {
            // remove the current element of the plainObject
            delete keys[index];

            self.set(key, plainObject[key])
                .then(function() {
                    results.push(arguments);

                    i++;
                    if (keys[i]) {
                        next(keys[i], i);
                    } else {
                        out();
                    }
                })
                .fail(function(err) {
                    errors.push(err);

                    i++;
                    if (keys[i]) {
                        return next(keys[i], i);
                    } else {
                        return out();
                    }
                });
        }

        function out() {
            if (errors.length) {
                callback(errors);
                promise.reject(errors);
            } else {
                callback(null, results);
                promise.resolve(results);
            }
        }

        next(keys[i], i);

        return promise;
    };

    /**
     * Set multiple keys to multiple values, only if none of the keys exist
     * @param  {Object}   plainObject      Object to set
     * @param  {Function} callback Callback
     * @return {Promise}           promise object
     */
    min.msetnx = function(plainObject, callback) {
        var promise = new Promise();

        var self = this;

        var keys = Object.keys(plainObject);
        var i = 0;

        var results = [];
        var errors = [];

        // Callback and Promise's shim
        callback = callback || utils.noop;

        function next(key, index) {
            delete keys[index];

            self.setnx(key, plainObject[key])
                .then(function() {
                    results.push(arguments);

                    i++;
                    if (keys[i]) {
                        next(keys[i], i);
                    } else {
                        out();
                    }
                })
                .fail(function(err) {
                    errors.push(err);
                    out();
                });
        }

        function out() {
            if (errors.length) {
                callback(errors);
                return promise.reject(errors);
            } else {
                callback(null, results);
                promise.resolve(results);
            }
        }

        next(keys[i], i);

        return promise;
    };

    /**
     * Append a value to a key
     * @param  {String}   key      key
     * @param  {String}   value    value
     * @param  {Function} callback Callback
     * @return {Promise}           promise
     */
    min.append = function(key, value, callback) {
        var self = this;
        var promise = new Promise();
        callback = callback || utils.noop;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    var p = new Promise();

                    p.resolve('');

                    return p;
                }
            })
            .then(function(currVal) {
                return self.set(key, currVal + value);
            })
            .then(function(key, value) {
                return self.strlen(key);
            })
            .then(function(len) {
                promise.resolve(len);
                callback(null, len);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    /**
     * Get the value of a key
     * @param  {String}   key      Key
     * @param  {Function} callback Callback
     * @return {Promise}           Promise Object
     */
    min.get = function(key, callback) {
        var self = this;

        // Promise Object
        var promise = new Promise(function(value) {
            self.emit('get', key, value);
        });

        // Store
        var store = this.store;

        // Callback and Promise's shim
        callback = callback || utils.noop;
        // Key prefix
        var $key = 'min-' + key;

        if (store.async) {
            // Async Store Operating

            // Value processing
            this.store.get($key, function(err, value) {
                if (err) {
                    var _err = new Error('no such key');
                    // Error!
                    promise.reject(_err);
                    return callback(_err);
                }

                if (value) {
                    // Done
                    var ret = JSON.parse(value);
                    promise.resolve(ret);
                    callback(null, ret);
                } else {
                    var err = new Error('no such key');

                    promise.resolve(err);
                    callback(err);
                }

            });
        } else {
            try {
                // Value processing
                var _value = this.store.get($key);

                if (_value) {
                    var value = JSON.parse(_value);
                    // Done
                    promise.resolve(value);
                    callback(null, value);
                } else {
                    var err = new Error('no such key');

                    promise.reject(err);
                    callback(err);
                }
            } catch(err) {
                // Error!
                promise.reject(err);
                callback(err);
            }
        }

        return promise;
    };

    min.getrange = function(key, start, end, callback) {
        var self = this;
        var promise = new Promise(function(value) {
            self.emit('getrange', key, start, end, value);
        });
        callback = callback || utils.noop;

        var len = end - start + 1;

        self.get(key)
            .then(function(value) {
                var val = value.substr(start, len);

                promise.resolve(val);
                callback(null, val);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    /**
     * Get the values of a set of keys
     * @param  {Array}   keys      the keys
     * @param  {Function} callback Callback
     * @return {Promise}           promise object
     */
    min.mget = function(keys, callback) {

        // Promise Object
        var promise = new Promise();

        var self = this;

        var i = 0;
        var results = [];
        var errors = [];

        // Callback and Promise's shim
        callback = callback || utils.noop;

        function next(key, index) {
            delete keys[index];

            self.get(key, function(err, value) {
                if (err) {
                    errors.push(err);
                    results.push(null);

                    i++;
                    if (keys[i]) {
                        return next(keys[i], i);
                    } else {
                        return out();
                    }
                }

                results.push(value);

                i++;
                if (keys[i]) {
                    next(keys[i], i);
                } else {
                    out();
                }
            });
        }

        function out() {
            if (errors.length) {
                promise.reject(errors);
            } else {
                promise.resolve(results);
            }
        }

        next(keys[i], i);

        return promise;
    };

    /**
     * Set the value of a key and return its old value
     * @param  {String}   key      key
     * @param  {Mix}   value       value
     * @param  {Function} callback Callback
     * @return {Promise}           promise object
     */
    min.getset = function(key, value, callback) {
        var self = this;
        var promise = new Promise(function(old) {
            self.emit('getset', key, value, old);
        });

        // Callback and Promise's shim
        if ('undefined' == typeof callback) {
            callback = utils.noop;
        }

        var _value = null;

        self.get(key)
            .then(function($value) {
                _value = $value;

                return self.set(key, value);
            })
            .then(function() {
                promise.resolve(_value);
                callback(null, _value);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    /**
     * Get the length of a key
     * @param  {String}   key      Key
     * @param  {Function} callback Callback
     * @return {Promise}           promise
     */
    min.strlen = function(key, callback) {
        var self = this;
        var promise = new Promise();
        callback = callback || utils.noop;

        self.get(key)
            .then(function(value) {
                if ('string' === typeof value) {
                    var len = value.length;

                    promise.resolve(len);
                    callback(null, len);
                } else {
                    var err = new TypeError();

                    promise.reject(err);
                    callback(err);
                }
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    /**
     * Increment the integer value of a key by one
     * @param  {String}   key      key
     * @param  {Function} callback callback
     * @return {Promise}           promise
     */
    min.incr = function(key, callback) {
        var self = this;

        var promise = new Promise(function(value) {
            self.emit('incr', key, value);
        });
        callback = callback || utils.noop;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    var p = new Promise();

                    p.resolve(0);

                    return p;
                }
            })
            .then(function(curr) {
                if (isNaN(parseInt(curr))) {
                    promise.reject('value wrong');
                    return callback('value wrong');
                }

                curr = parseInt(curr);

                return self.set(key, ++curr);
            })
            .then(function(key, value) {
                promise.resolve(value);
                callback(null, value);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    /**
     * Increment the integer value of a key by the given amount
     * @param  {String}   key      key
     * @param  {Number}   increment increment
     * @param  {Function} callback callback
     * @return {Promise}           promise
     */
    min.incrby = function(key, increment, callback) {
        var self = this;
        var promise = new Promise(function(value) {
            self.emit('incrby', key, value);
        });
        callback = callback || utils.noop;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    var p = new Promise();

                    p.resolve(0);

                    return p;
                }
            })
            .then(function(curr) {
                if (isNaN(parseFloat(curr))) {
                    promise.reject('value wrong');
                    return callback('value wrong');
                }

                curr = parseFloat(curr);

                return self.set(key, curr + increment);
            })
            .then(function(key, value) {
                promise.resolve(value);
                callback(null, value);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    min.incrbyfloat = min.incrby;

    min.decr = function(key, callback) {
        var self = this;
        var promise = new Promise(function(curr) {
            self.emit('decr', key, curr);
        });
        callback = callback || utils.noop;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    var p = new Promise();

                    p.resolve(0);

                    return p;
                }
            })
            .then(function(curr) {
                if (isNaN(parseInt(curr))) {
                    promise.reject('value wrong');
                    return callback('value wrong');
                }

                curr = parseInt(curr);

                return self.set(key, --curr);
            })
            .done(function(key, value) {
                promise.resolve(value);
                callback(null, value);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    min.decrby = function(key, decrement, callback) {
        var self = this;
        var promise = new Promise(function(curr) {
            self.emit('decrby', key, decrement, curr);
        });
        callback = callback || utils.noop;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    var p = new Promise();

                    p.resolve(0);

                    return p;
                }
            })
            .then(function(curr) {
                if (isNaN(parseInt(curr))) {
                    promise.reject('value wrong');
                    return callback('value wrong');
                }

                curr = parseInt(curr);

                return self.set(key, curr - decrement);
            })
            .done(function(key, value) {
                promise.resolve(value);
                callback(null, value);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    /******************************
     **           Hash            **
     ******************************/

    /**
     * Set the field in the hash on the key with the value
     * @param  {String}   key      Hash key
     * @param  {String}   field    field to set
     * @param  {Mix}   value       value
     * @param  {Function} callback Callback
     * @return {Promise}           promise object
     */
    min.hset = function(key, field, value, callback) {
        var self = this;
        var promise = new Promise(function() {
            self.emit('hset', key, field, value);
        });

        callback = callback || utils.noop;

        // check the key status
        self.exists(key, function(err, exists) {
            if (err) {
                promise.reject(err);
                return callback(err);
            }

            if (exists) {
                // fetch the value
                self.get(key, function(err, body) {
                    if (err) {
                        promise.reject(err);
                        return callback(err);
                    }

                    // update the hash
                    body[field] = value;

                    self.set(key, body, function(err, _key, _value) {
                        if (err) {
                            promise.reject(err);
                            return callback(err);
                        }

                        promise.resolve(key, field, value);
                        callback(null, key, field, value);
                    });
                });
            } else {
                // create a hash
                var body = {};

                body[field] = value;

                self.set(key, body, function(err, _key, _value) {
                    if (err) {
                        promise.reject(err);
                        return callback(err);
                    }

                    _keys[key] = 1;

                    promise.resolve(key, field, value);
                    callback(null, key, field, value);
                });
            }

        });

        return promise;
    };

    /**
     * Set the value of a hash field, only if the field does not exist
     * @param  {String}   key      key
     * @param  {String}   field    hash field
     * @param  {Mix}   value       value
     * @param  {Function} callback Callback
     * @return {Promise}            promise
     */
    min.hsetnx = function(key, field, value, callback) {
        var promise = new Promise();
        var self = this;
        callback = callback || utils.noop;

        self.hexists(key, field, function(err, exists) {
            if (err) {
                promise.reject(err);
                return callback(err);
            }

            if (!exists) {
                self.hset(key, field, value, function(err) {
                    if (err) {
                        promise.reject(err);
                        callback(err);
                    }

                    promise.resolve('OK');
                    callback(null, 'OK');
                });
            } else {
                var err = new Error('The field of the hash is exists');

                promise.reject(err);
                return callback(err);
            }
        });

        return promise;
    };

    /**
     * Set multiple hash fields to multiple values
     * @param  {String}   key      key
     * @param  {Object}   docs     values
     * @param  {Function} callback Callback
     * @return {Promise}           promise
     */
    min.hmset = function(key, docs, callback) {
        var promise = new Promise();
        var self = this;
        callback = callback || utils.noop;

        var keys = Object.keys(docs);
        var replies = [];

        var multi = min.multi();

        keys.forEach(function(field) {
            multi.hset(key, field, docs[field]);
        });

        multi.exec(function(err, replies) {
            callback(null, replies);
            promise.resolve(replies);
        });

        return promise;
    };

    /**
     * Get the value of a hash field
     * @param  {String}   key      key
     * @param  {String}   field    hash field
     * @param  {Function} callback Callback
     * @return {Promise}           promise
     */
    min.hget = function(key, field, callback) {
        var promise = new Promise();
        var self = this;
        callback = callback || utils.noop;

        self.hexists(key, field, function(err, exists) {
            if (err) {
                promise.reject(err);
                return callback(err);
            }

            if (exists) {
                self.get(key)
                    .done(function(value) {
                        var data = value[field];
                        promise.resolve(data);
                        callback(null, data);
                    })
                    .fail(function(err) {
                        promise.reject(err);
                        callback(err);
                    });
            } else {
                var err = new Error('no such field');

                promise.reject(err);
                callback(err);
            }
        });

        return promise;
    };

    /**
     * Get the values of all the given hash fields
     * @param  {String}   key      key
     * @param  {Array}   fields    hash fields
     * @param  {Function} callback Callback
     * @return {Promise}           promise
     */
    min.hmget = function(key, fields, callback) {
        var promise = new Promise();
        var self = this;
        callback = callback || utils.noop;

        var values = [];

        var multi = min.multi();

        fields.forEach(function(field) {
            multi.hget(key, field);
        });

        multi.exec(function(err, replies) {
            if (err) {
                callback(err);
                return promise.reject(err);
            }

            values = replies.map(function(row) {
                return row[0];
            });

            promise.resolve(values);
            callback(null, values);
        });

        return promise;
    };

    /**
     * Get all the fields and values in a hash
     * @param  {String}   key      key
     * @param  {Function} callback Callback
     * @return {Promise}           promise
     */
    min.hgetall = function(key, callback) {
        var promise = new Promise();
        var self = this;
        callback = callback || utils.noop;

        self.exists(key, function(err, exists) {
            if (err) {
                callback(err);
                return promise.reject(err);
            }

            if (exists) {
                self.get(key)
                    .done(function(data) {
                        promise.resolve(data);
                        callback(null, data);
                    })
                    .fail(function(err) {
                    });
            } else {
                var err = new Error('no such key');

                callback(err);
                return promise.reject(err);
            }
        });

        return promise;
    };

    /**
     * Delete one hash field
     * @param  {String}   key      key
     * @param  {String}   field    hash field
     * @param  {Function} callback Callback
     * @return {Promise}           promise
     */
    min.hdel = function(key, field, callback) {
        var self = this;
        var promise = new Promise(function(key, field, value) {
            self.emit('hdel', key, field, value);
        });
        callback = callback || utils.noop;

        self.hexists(key. field, function(err, exists) {
            if (err) {
                callback(err);
                return promise.reject(err);
            }

            if (exists) {
                self.get(key)
                    .done(function(data) {
                        var removed = data[field];
                        delete data[field];

                        self.set(key, data)
                            .done(function() {
                                promise.resolve(key, field, removed);
                                callback(null, key, field, removed);
                            })
                            .fail(function(err) {
                                promise.reject(err);
                                callback(err);
                            });
                    })
                    .fail(function(err) {
                        callback(err);
                    })
            } else {
                var err = new Error('no such key');

                callback(err);
                return promise.reject(err);
            }
        });

        return promise;
    };

    /**
     * Get the number of fields in a hash
     * @param  {String}   key      key
     * @param  {Function} callback Callback
     * @return {Promise}           promise
     */
    min.hlen = function(key, callback) {
        var promise = new Promise();
        var self = this;
        callback = callback || utils.noop;

        self.exists(key, function(err, exists) {
            if (err) {
                promise.reject(err);
                return callback(err);
            }

            if (exists) {
                self.get(key)
                    .done(function(data) {
                        var length = Object.keys(data).length;

                        promise.resolve(length);
                        callback(null, length);
                    })
                    .fail(function(err) {
                        promise.reject(err);
                        callback(err);
                    });
            } else {
                promise.resolve(0);
                callback(null, 0);
            }
        });

        return promise;
    };

    /**
     * Get all the fields in a hash
     * @param  {String}   key      key
     * @param  {Function} callback Callback
     * @return {Promise}           promise
     */
    min.hkeys = function(key, callback) {
        var promise = new Promise();
        var self = this;
        callback = callback || utils.noop;

        self.exists(key, function(err, exists) {
            if (err) {
                promise.reject(err);
                return callback(err);
            }

            if (exists) {
                self.get(key)
                    .done(function(data) {
                        var keys = Object.keys(data);

                        promise.resolve(keys);
                        callback(null, keys);
                    })
                    .fail(function(err) {
                        promise.reject(err);
                        callback(err);
                    });
            } else {
                promise.resolve(0);
                callback(null, 0);
            }
        });

        return promise;
    };

    /**
     * Determine if a hash field exists
     * @param  {String}   key      key of the hash
     * @param  {String}   field    the field
     * @param  {Function} callback Callback
     * @return {Promise}           promise object
     */
    min.hexists = function(key, field, callback) {
        var promise = new Promise();
        var self = this;

        // Callback and Promise's shim
        callback = callback || utils.noop;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    promise.resolve(false);
                    callback(null, false);
                }
            })
            .then(function(value) {
                if (value.hasOwnProperty(field)) {
                    promise.resolve(true);
                    callback(null, true);
                } else {
                    promise.resolve(false);
                    callback(null, false);
                }
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    min.hincr = function(key, field, callback) {
        var self = this;
        var promise = new Promise(function(curr) {
            self.emit('hincr', key, field, curr);
        });
        callback = callback || utils.noop;

        self.hexists(key, field)
            .then(function(exists) {
                if (exists) {
                    return self.hget(exists);
                } else {
                    var p = new Promise();

                    p.resolve(0);

                    return p;
                }
            })
            .then(function(curr) {
                if (isNaN(parseFloat(curr))) {
                    promise.reject('value wrong');
                    return callback('value wrong');
                }

                curr = parseFloat(curr);

                return self.hset(key, field, ++curr);
            })
            .then(function(key, field, value) {
                promise.resolve(value);
                callback(null, value);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(null, err);
            });

        return promise;
    };

    min.hincrby = function(key, field, increment, callback) {
        var self = this;
        var promise = new Promise(function(curr) {
            self.emit('hincr', key, field, curr);
        });
        callback = callback || utils.noop;

        self.hexists(key, field)
            .then(function(exists) {
                if (exists) {
                    return self.hget(exists);
                } else {
                    var p = new Promise();

                    p.resolve(0);

                    return p;
                }
            })
            .then(function(curr) {
                if (isNaN(parseFloat(curr))) {
                    promise.reject('value wrong');
                    return callback('value wrong');
                }

                curr = parseFloat(curr);

                return self.hset(key, field, curr + increment);
            })
            .then(function(key, field, value) {
                promise.resolve(value);
                callback(null, value);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(null, err);
            });

        return promise;
    };

    min.hincrbyfloat = min.hincrby;

    min.hdecr = function(key, field, callback) {
        var self = this;
        var promise = new Promise(function(curr) {
            self.emit('hdecr', key, field, curr);
        });
        callback = callback || utils.noop;

        self.hexists(key, field)
            .then(function(exists) {
                if (exists) {
                    return self.hget(key, field);
                } else {
                    var p = new Promise();

                    p.resolve(0);

                    return p;
                }
            })
            .then(function(curr) {
                if (isNaN(parseFloat(curr))) {
                    promise.reject('value wrong');
                    return callback('value wrong');
                }

                curr = parseFloat(curr);

                return self.hset(key, field, --curr);
            })
            .then(function(key, field, value) {
                promise.resolve(value);
                callback(null, value);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    min.hdecrby = function(key, field, decrement, callback) {
        var self = this;
        var promise = new Promise(function(curr) {
            self.emit('hincr', key, field, curr);
        });
        callback = callback || utils.noop;

        self.hexists(key, field)
            .then(function(exists) {
                if (exists) {
                    return self.hget(exists);
                } else {
                    var p = new Promise();

                    p.resolve(0);

                    return p;
                }
            })
            .then(function(curr) {
                if (isNaN(parseFloat(curr))) {
                    promise.reject('value wrong');
                    return callback('value wrong');
                }

                curr = parseFloat(curr);

                return self.hset(key, field, curr - decrement);
            })
            .then(function(key, field, value) {
                promise.resolve(value);
                callback(null, value);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(null, err);
            });

        return promise;
    };

    /******************************
     **           List            **
     ******************************/

    /**
     * Prepend one or multiple values to a list
     * @param  {String}   key      key
     * @param  {Mix}   value       value
     * @param  {Function} callback Callback
     * @return {Promise}           promise
     */
    min.lpush = function(key, value, callback) {
        var self = this;
        var promise = new Promise(function(len) {
            self.emit('lpush', key, len);
        });
        callback = callback || utils.noop;

        self.exists(key, function(err, exists) {
            if (err) {
                promise.reject(err);
                return callback(err);
            }

            if (exists) {
                self.get(key, function(err, data) {
                    if (err) {
                        promise.reject(err);
                        return callback(err);
                    }

                    data.unshift(value);

                    self.set(key, data, function(err) {
                        if (err) {
                            promise.reject(err);
                            return callback(err);
                        }

                        var length = data.length;

                        promise.resolve(length);
                        callback(null, length);
                    });
                });
            } else {
                var data = [ value ];

                self.set(key, data, function(err) {
                    if (err) {
                        promise.reject(err);
                        return callback(err);
                    }

                    _keys[key] = 2;

                    promise.resolve(1);
                    callback(null, 1);
                });
            }
        });

        return promise;
    };

    /**
     * Prepend a value to a list, only if the list exists
     * @param  {String}   key      key
     * @param  {Mix}   value       value
     * @param  {Function} callback Callback
     * @return {Promise}           promise
     */
    min.lpushx = function(key, value, callback) {
        var promise = new Promise();
        callback = callback || utils.noop;
        var self = this;

        self.exists(key, function(err, exists) {
            if (err) {
                promise.reject(err);
                return callback(err);
            }

            if (exists) {
                self.get(key, function(err, data) {
                    if (err) {
                        promise.reject(err);
                        return callback(err);
                    }

                    if (!data.length) {
                        var err = new Error('The list is empty.');

                        callback(err);
                        return promise.reject(err);
                    }

                    data.unshift(value);

                    self.set(key, data, function(err) {
                        if (err) {
                            promise.reject(err);
                            return callback(err);
                        }

                        var length = data.length;

                        promise.resolve(length);
                        callback(null, length);
                    });
                });
            } else {
                var err = new Error('no such key');

                callback(err);
                return promise.reject(err);
            }
        });

        return promise;
    };

    /**
     * Append one or multiple values to a list
     * @param  {String}   key      key
     * @param  {Mix}   value       value
     * @param  {Function} callback Callback
     * @return {Promise}           promise
     */
    min.rpush = function(key, value, callback) {
        var self = this;
        var promise = new Promise(function(len) {
            self.emit('rpush', key, len);
        });
        callback = callback || utils.noop;

        self.exists(key, function(err, exists) {
            if (err) {
                promise.reject(err);
                return callback(err);
            }

            if (exists) {
                self.get(key, function(err, data) {
                    if (err) {
                        promise.reject(err);
                        return callback(err);
                    }

                    data.push(value);

                    self.set(key, data, function(err) {
                        if (err) {
                            promise.reject(err);
                            return callback(err);
                        }

                        var length = data.length;

                        promise.resolve(length);
                        callback(null, length);
                    });
                });
            } else {
                var data = [ value ];

                self.set(key, data, function(err) {
                    if (err) {
                        promise.reject(err);
                        return callback(err);
                    }

                    promise.resolve(1);
                    callback(null, 1);
                });
            }
        });

        return promise;
    };

    /**
     * Prepend a value to a list, only if the list exists
     * @param  {String}   key      key
     * @param  {Mix}   value       value
     * @param  {Function} callback Callback
     * @return {Promise}           promise
     */
    min.lpushx = function(key, value, callback) {
        var promise = new Promise();
        callback = callback || utils.noop;
        var self = this;

        self.exists(key, function(err, exists) {
            if (err) {
                promise.reject(err);
                return callback(err);
            }

            if (exists) {
                self.get(key, function(err, data) {
                    if (err) {
                        promise.reject(err);
                        return callback(err);
                    }

                    if (!data.length) {
                        var err = new Error('The list is empty.');

                        callback(err);
                        return promise.reject(err);
                    }

                    data.push(value);

                    self.set(key, data, function(err) {
                        if (err) {
                            promise.reject(err);
                            return callback(err);
                        }

                        var length = data.length;

                        promise.resolve(length);
                        callback(null, length);
                    });
                });
            } else {
                var err = new Error('no such key');

                callback(err);
                return promise.reject(err);
            }
        });

        return promise;
    };

    /**
     * Remove and get the first element in a list
     * @param  {String}   key      key
     * @param  {Function} callback Callback
     * @return {Promise}           promise
     */
    min.lpop = function(key, callback) {
        var self = this;
        var promise = new Promise(function(value) {
            self.emit('lpop', key, value);
        });
        callback = callback || utils.noop;
        var val = null;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    promise.resolve(null);
                    callback(null, null);
                }
            })
            .then(function(data) {
                val = data.shift();

                return self.set(key,data);
            })
            .then(function() {
                promise.resolve(val);
                callback(null, val);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    /**
     * Remove and get the last element in a list
     * @param  {String}   key      key
     * @param  {Function} callback Callback
     * @return {Promise}           promise
     */
    min.rpop = function(key, callback) {
        var self = this;
        var promise = new Promise(function(value) {
            self.emit('rpop', key, value);
        });
        callback = callback || utils.noop;

        var value = null;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    promise.resolve(null);
                    callback(null, null);
                }
            })
            .then(function(data) {
                value = data.pop();

                return self.set(key, data);
            })
            .then(function() {
                promise.resolve(value);
                callback(null, value);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    /**
     * Get the length of a list
     * @param  {String}   key      key
     * @param  {Function} callback callback
     * @return {Promise}           promise
     */
    min.llen = function(key, callback) {
        var promise = new Promise();
        callback = callback || utils.noop;
        var self = this;

        self.exists(key, function(err, exists) {
            if (err) {
                promise.reject(err);
                return callback(err);
            }

            if (exists) {
                self.get(key, function(err, data) {
                    if (err) {
                        promise.reject(err);
                        return callback(err);
                    }

                    var length = data.length;

                    promise.resolve(length);
                    callback(null, length);
                });
            } else {
                promise.resolve(0);
                callback(null, 0);
            }
        });

        return promise;
    };

    /**
     * Get a range of elements from a list
     * @param  {String}   key      key
     * @param  {Number}   start    min score
     * @param  {Number}   stop     max score
     * @param  {Function} callback callback
     * @return {Promise}           promise
     */
    min.lrange = function(key, start, stop, callback) {
        var promise = new Promise();
        callback = callback || utils.noop;
        var self = this;

        self.exists(key, function(err, exists) {
            if (err) {
                promise.reject(err);
                return callback(err);
            }

            if (exists) {
                self.get(key, function(err, data) {
                    if (err) {
                        promise.reject(err);
                        return callback(err);
                    }

                    var values = data.slice(start, stop + 1);

                    promise.resolve(values);
                    callback(null, values);
                });
            } else {
                promise.resolve(null);
                callback(null, null);
            }
        });

        return promise;
    };

    /**
     * Remove elements from a list
     * @param  {String}   key      key
     * @param  {Number}   count    count to remove
     * @param  {Mix}      value    value
     * @param  {Function} callback callback
     * @return {Promise}           promise
     */
    min.lrem = function(key, count, value, callback) {
        var self = this;
        var promise = new Promise(function(removeds) {
            self.emit('lrem', key, removeds);
        });
        callback = callback || utils.noop;

        self.exists(key, function(err, exists) {
            if (err) {
                promise.reject(err);
                return callback(err);
            }

            if (exists) {
                self.get(key, function(err, data) {
                    if (err) {
                        promise.reject(err);
                        return callback(err);
                    }

                    var removeds = 0;

                    switch (true) {
                        case count > 0:
                            for (var i = 0; i < data.length && removeds < count; i++) {
                                if (data[i] === value) {
                                    var removed = data.splice(i, 1)[0];

                                    removeds++;
                                }
                            }
                            break;
                        case count < 0:
                            for (var i = data.length - 1; i >= 0 && removeds < count; i--) {
                                if (data[i] === value) {
                                    var removed = data.splice(i, 1)[0];

                                    removeds++;
                                }
                            }
                            break;
                        case count = 0:
                            for (var i = data.length - 1; i >= 0; i--) {
                                if (data[i] === value) {
                                    var removed = data.splice(i, 1)[0];

                                    removeds++;
                                }
                            }
                            break;
                    }

                    promise.resolve(removeds);
                    callback(null, removeds);
                });
            } else {
                promise.resolve(null);
                callback(null, null);
            }
        });

        return promise;
    };

    /**
     * Remove elements from a list
     * @param  {String}   key      key
     * @param  {Number}   index    position to set
     * @param  {Mix}      value    value
     * @param  {Function} callback callback
     * @return {Promise}           promise
     */
    min.lset = function(key, index, value, callback) {
        var self = this;
        var promise = new Promise(function(len) {
            self.emit('lset', key, len);
        });
        callback = callback || utils.noop;

        self.exists(key, function(err, exists) {
            if (err) {
                promise.reject(err);
                return callback(err);
            }

            if (exists) {
                self.get(key, function(err, data) {
                    if (err) {
                        promise.reject(err);
                        return callback(err);
                    }

                    if (!data[index] || !data.length) {
                        var err = new Error('no such key');

                        promise.reject(err);
                        return callback(err);
                    }

                    data[index] = value;

                    self.set(key, data, function(err) {
                        if (err) {
                            promise.reject(err);
                            return callback(err);
                        }

                        var length = data.length;

                        promise.resolve(length);
                        callback(null, length);
                    });
                });
            } else {
                var err = new Error('no such key');

                promise.reject(err);
                return callback(err);
            }
        });

        return promise;
    };

    /**
     * Trim a list to the specified range
     * @param  {String}   key      key
     * @param  {Number}   start    start
     * @param  {Number}   stop     stop
     * @param  {Function} callback callback
     * @return {Promise}           promise
     */
    min.ltrim = function(key, start, stop, callback) {
        var promise = new Promise();
        var self = this;
        callback = callback || utils.noop;

        self.exists(key, function(err, exists) {
            if (err) {
                promise.reject(err);
                return callback(err);
            }

            if (exists) {
                self.get(key, function(err, data) {
                    if (err) {
                        promise.reject(err);
                        return callback(err);
                    }

                    var values = data.splice(start, stop + 1);

                    promise.resolve(values);
                    callback(null, values);
                });
            } else {
                promise.resolve(null);
                callback(null, null);
            }
        });

        return promise;
    };

    /**
     * Get an element from a list by its index
     * @param  {String}   key      key
     * @param  {Number}   index    index
     * @param  {Function} callback callback
     * @return {Promise}           promise
     */
    min.lindex = function(key, index, callback) {
        var promise = new Promise();
        var self = this;
        callback = callback || utils.noop;

        self.exists(key, function(err, exists) {
            if (err) {
                promise.reject(err);
                return callback(err);
            }

            if (exists) {
                self.get(key, function(err, data) {
                    if (err) {
                        promise.reject(err);
                        return callback(err);
                    }

                    var value = data[index];

                    promise.resolve(value);
                    callback(null, value);
                });
            } else {
                promise.resolve(null);
                callback(null, null);
            }
        });

        return promise;
    };

    /**
     * Insert an element before another element in a list
     * @param  {String}   key      key
     * @param  {Mix}   pivot       pivot
     * @param  {Mix}   value       value
     * @param  {Function} callback callback
     * @return {Promise}           promise
     */
    min.linsertBefore = function(key, pivot, value, callback) {
        var promise = new Promise();
        var self = this;
        callback = callback || utils.noop;

        self.exists(key, function(err, exists) {
            if (err) {
                promise.reject(err);
                return callback(err);
            }

            if (exists) {
                self.get(key, function(err, data) {
                    if (err) {
                        promise.reject(err);
                        return callback(err);
                    }

                    var index = data.indexOf(pivot);

                    if (index < 0) {
                        promise.resolve(null);
                        return callback(null, null);
                    }

                    data.splice(index, 1, value, pivot);

                    self.set(key, data, function(err) {
                        if (err) {
                            promise.reject(err);
                            return callback(err);
                        }

                        var length = data.length;

                        promise.resolve(length);
                        callback(null, length);
                    });
                });
            } else {
                promise.resolve(null);
                callback(null, null);
            }
        });

        return promise;
    };

    /**
     * Insert an element after another element in a list
     * @param  {String}   key      key
     * @param  {Mix}   pivot       pivot
     * @param  {Mix}   value       value
     * @param  {Function} callback callback
     * @return {Promise}           promise
     */
    min.linsertAfter = function(key, pivot, value, callback) {
        var promise = new Promise();
        var self = this;
        callback = callback || utils.noop;

        self.exists(key, function(err, exists) {
            if (err) {
                promise.reject(err);
                return callback(err);
            }

            if (exists) {
                self.get(key, function(err, data) {
                    if (err) {
                        promise.reject(err);
                        return callback(err);
                    }

                    var index = data.indexOf(pivot);

                    if (index < 0) {
                        promise.resolve(null);
                        return callback(null, null);
                    }

                    data.splice(index, 0, value);

                    self.set(key, data, function(err) {
                        if (err) {
                            promise.reject(err);
                            return callback(err);
                        }

                        var length = data.length;

                        promise.resolve(length);
                        callback(null, length);
                    });
                });
            } else {
                promise.resolve(null);
                callback(null, null);
            }
        });

        return promise;
    };

    /**
     * Remove the last element in a list, append it to another list and return it
     * @param  {String}   src      source
     * @param  {String}   dest     destination
     * @param  {Function} callback callback
     * @return {Promise}           promise
     */
    min.rpoplpush = function(src, dest, callback) {
        var promise = new Promise();
        var self = this;
        callback = callback || utils.noop;

        self.rpop(src)
            .then(function(value) {
                return self.lpush(dest, value)
            })
            .then(function(length) {
                promise.resolve(value, length);
                callback(null, value, length);
            })
            .fail(function(err) {
                callback(err);
                promise.reject(err);
            });

        return promise;
    };

    /******************************
     **           Set             **
     ******************************/
    min.sadd = function(key, members) {
        var self = this;
        var promise = new Promise(function(len) {
            self.emit('sadd', key, len);
        });

        members = Array.isArray(members) ? members : [].slice.call(arguments, 1);
        var added = 0;

        if (!(members[members.length - 1] instanceof Function)) {
            var callback = utils.noop;
        } else {
            var callback = members.splice(members.length - 1, 1)[0];
        }

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    var data = utils.arrayUnique(members);

                    return self.set(key, data);
                }
            })
            .then(function() {
                if (Array.isArray(arguments[0])) {
                    var data = arguments[0];

                    for (var i = 0; i < members.length; i++) {
                        (function(index) {
                            var curr = members[index];

                            if (data.indexOf(curr) >= 0) {
                                return;
                            } else {
                                data.push(curr);
                                added++;
                            }
                        })(i);
                    }

                    return self.set(key, data);
                } else if (typeof arguments[0] === 'string') {
                    added += members.length;

                    _keys[key] = 3;

                    promise.resolve(added);
                    callback(null, added);
                }
            })
            .then(function() {

                _keys[key] = 3;

                promise.resolve(added);
                callback(null, added);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    min.srem = function(key, members, callback) {
        var self = this;
        var promise = new Promise(function(len) {
            self.emit('srem', key, len);
        });

        members = [].slice.call(arguments, 1);
        var removeds = 0;

        if (!(members[members.length - 1] instanceof Function)) {
            callback = utils.noop;
        }

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    return new Error('no such key');
                }
            })
            .then(function(data) {
                for (var i = 0; i < members.length; i++) {
                    (function(index) {
                        var curr = members[index];

                        var i = data.indexOf(curr);
                        if (i >= 0) {
                            data.splice(i, 1);
                            removeds++;
                        }
                    })(i);
                }

                return self.set(key, data);
            })
            .then(function() {

                _keys[key] = 3;

                promise.resolve(removeds);
                callback(null, removeds);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    min.smembers = function(key, callback) {
        var promise = new Promise();
        var self = this;
        callback = callback || utils.noop;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    return new Error('no such key');
                }
            })
            .then(function(members) {
                promise.resolve(members);
                callback(null, members);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    min.sismember = function(key, value, callback) {
        var self = this;
        var promise = new Promise();

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    return new Error('no such key');
                }
            })
            .then(function(members) {
                var res = members.indexOf(value) >= 0 ? 1 : 0;

                promise.resolve(res);
                callback(null, res);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    min.scard = function(key, callback) {
        var promise = new Promise();
        callback = callback || utils.noop;
        var self = this;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    return new Error('no such key');
                }
            })
            .then(function(data) {
                var length = data.length;

                promise.resolve(length);
                callback(null, length);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    min.smove = function(src, dest, member, callback) {
        var self = this;
        var promise = new Promise(function(ok) {
            self.emit('smove', src, dest, member, ok);
        });

        members = [].slice.call(arguments, 1);

        if (!(members[members.length - 1] instanceof Function)) {
            callback = utils.noop;
        }

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.sismember(src, member);
                } else {
                    return new Error('no such key');
                }
            })
            .then(function(isMember) {
                if (isMember) {
                    return self.srem(src, member);
                } else {
                    return new Error('no such member');
                }
            })
            .then(function() {
                return self.sismember(dest, member);
            })
            .then(function(isMember) {
                if (!isMember) {
                    return self.sadd(dest, member);
                } else {

                    _keys[key] = 3;

                    promise.resolve(0);
                    callback(null, 0);
                }
            })
            .then(function() {
                _keys[key] = 3;
                promise.resolve(1);
                callback(null, 1);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    min.srandmember = function(key, callback) {
        var promise = new Promise();
        var self = this;
        callback = callback || utils.noop;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    promise.resolve(null);
                    callback(null, null);
                }
            })
            .then(function(members) {
                var index = Math.random() * members.length | 0;

                var member = members[index];

                promise.resolve(member);
                callback(null, member);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    min.spop = function(key, callback) {
        var self = this;
        var promise = new Promise(function(value) {
            self.emit('spop', key, value);
        });
        callback = callback || utils.noop;

        var member = null;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.srandmember(key);
                } else {
                    promise.resolve(null);
                    callback(null, null);
                }
            })
            .then(function(_member) {
                member = _member;

                return self.srem(key, member);
            })
            .then(function() {
                promise.resolve(member);
                callback(null, member);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    min.sunion = function(keys, callback) {
        var promise = new Promise();
        var self = this;

        keys = [].slice.call(arguments);

        if (!(keys[keys.length - 1] instanceof Function)) {
            callback = utils.noop;
        }

        var members = [];

        (function loop(index) {
            var curr = keys[index];

            if (curr) {
                self.exists(curr)
                    .then(function(exists) {
                        if (exists) {
                            return self.get(curr);
                        } else {
                            loop(++index);
                        }
                    })
                    .then(function(data) {
                        if (Array.isArray(data)) {
                            members = members.concat(data);
                        }

                        loop(++index);
                    })
                    .fail(function(err) {
                        promise.reject(err);
                        return callback(err);
                    });
            } else {
                members = utils.arrayUnique(members);
                promise.resolve(members);
                callback(null, members);
            }
        })(0);

        return promise;
    };

    min.sunionstore = function(dest, keys, callback) {
        var promise = new Promise();
        var self = this;

        keys = [].slice.call(arguments, 2);

        if (!(keys[keys.length - 1] instanceof Function)) {
            callback = utils.noop;
        }

        var members = null;

        self.sunion(keys)
            .then(function(_members) {
                members = _members;

                return self.exists(dest);
            })
            .then(function(exists) {
                if (exists) {
                    return self.del(dest);
                } else {
                    return self.sadd(dest, members);
                }
            })
            .then(function(length) {
                if (typeof length == 'number') {
                    promise.resolve(length, members);
                    callback(null, length, members);
                } else {
                    return self.sadd(dest, members);
                }
            })
            .then(function(length) {
                promise.resolve(length, members);
                callback(null, length, members);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    min.sinter = function(keys, callback) {
        var promise = new Promise();
        var self = this;

        keys = [].slice.call(arguments);

        if (!(keys[keys.length - 1] instanceof Function)) {
            callback = utils.noop;
        }

        var memberRows = [];

        (function loop(index) {
            var curr = keys[index];

            if (curr) {
                self.exists(curr)
                    .then(function(exists) {
                        if (exists) {
                            return self.get(curr);
                        } else {
                            loop(++index);
                        }
                    })
                    .then(function(data) {
                        if (Array.isArray(data)) {
                            memberRows.push(data);
                        }

                        loop(++index);
                    })
                    .fail(function(err) {
                        promise.reject(err);
                        return callback(err);
                    });
            } else {
                var members = utils.arrayInter.apply(utils, memberRows);

                promise.resolve(members);
                callback(null, members);
            }
        })(0);

        return promise;
    };

    min.sinterstore = function(dest, keys, callback) {
        var promise = new Promise();
        var self = this;

        keys = [].slice.call(arguments, 1);

        if (!(keys[keys.length - 1] instanceof Function)) {
            callback = utils.noop;
        }

        var members = null;

        self.sinter.apply(self, keys)
            .then(function(_members) {
                members = _members;

                return self.exists(dest);
            })
            .then(function(exists) {
                if (exists) {
                    return self.del(dest);
                } else {
                    members.unshift(dest);
                    return self.sadd.apply(self, members);
                }
            })
            .then(function(key) {
                if (typeof key == 'string') {
                    promise.resolve(members.length, members);
                    callback(null, members.length, members);
                } else {
                    return self.sadd(dest, members);
                }
            })
            .then(function() {
                promise.resolve(members.length, members);
                callback(null, members.length, members);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    min.sdiff = function(keys, callback) {
        var promise = new Promise();
        var self = this;

        keys = [].slice.call(arguments, 1);

        if (!(keys[keys.length - 1] instanceof Function)) {
            callback = utils.noop;
        }

        var memberRows = [];

        (function loop(index) {
            var curr = keys[index];

            if (curr) {
                self.exists(curr)
                    .then(function(exists) {
                        if (exists) {
                            return self.get(curr);
                        } else {
                            loop(++index);
                        }
                    })
                    .then(function(data) {
                        if (Array.isArray(data)) {
                            memberRows.push(data);
                        }

                        loop(++index);
                    })
                    .fail(function(err) {
                        promise.reject(err);
                        return callback(err);
                    });
            } else {
                var members = utils.arrayDiff.apply(utils, memberRows);

                promise.resolve(members);
                callback(null, members);
            }
        })(0);

        return promise;
    };

    min.sdiffstore = function(dest, keys, callback) {
        var promise = new Promise();
        var self = this;

        keys = [].slice.call(arguments, 2);

        if (!(keys[keys.length - 1] instanceof Function)) {
            callback = utils.noop;
        }

        var members = null;

        self.sdiff(keys)
            .then(function(_members) {
                members = _members;

                return self.exists(dest);
            })
            .then(function(exists) {
                if (exists) {
                    return self.del(dest);
                } else {
                    return self.sadd(dest, members);
                }
            })
            .then(function(length) {
                if (typeof length == 'number') {
                    promise.resolve(length, members);
                    callback(null, length, members);
                } else {
                    return self.sadd(dest, members);
                }
            })
            .then(function(length) {
                promise.resolve(length, members);
                callback(null, length, members);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    /**
     * TODO: Sorted Set
     */

    /******************************
     **         Sorted Set        **
     ******************************/
    min.zadd = function(key, score, member, callback) {
        var self = this;
        var promise = new Promise(function(len) {
            self.emit('zadd', len);
        });
        callback = callback || utils.noop;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    var score2HashsMap = {};
                    score2HashsMap[score] = [ 0 ];

                    return self.set(key, {
                        // members
                        ms: [ member ],
                        // mapping hash to score
                        hsm: { 0: score },
                        // mapping score to hash
                        shm: score2HashsMap
                    });
                }
            })
            .then(function(_key) {
                if ('string' === typeof _key) {
                    _keys[key] = 4;

                    promise.resolve(1, 1);
                    callback(null, 1, 1);
                } else if ('object' === typeof _key) {
                    var data = _key;

                    if (data.ms.indexOf(member) >= 0) {
                        var len = data.ms.length;

                        promise.resolve(0, len);
                        return callback(null, 0, len);
                    }

                    // new hash
                    var hash = data.ms.length;
                    // append the new member
                    data.ms.push(member);

                    // mapping hash to score
                    data.hsm[hash] = score;

                    // mapping score to hash
                    if (Array.isArray(data.shm[score])) {
                        data.shm[score].push(hash);
                    } else {
                        data.shm[score] = [ hash ];
                    }

                    return self.set(key, data);
                }
            })
            .then(function(key, data) {
                _keys[key] = 4;

                var len = data.ms.length;

                promise.resolve(1, len);
                callback(null, 1, len);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    min.zcard = function(key, callback) {
        var self = this;
        var promise = new Promise();
        callback = callback || utils.noop;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    var err = new Error('no such key');

                    promise.reject(err);
                    callback(err);
                }
            })
            .then(function(data) {
                var len = data.ms.filter(Boolean).length;

                promise.resolve(len);
                callback(null, len);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    min.zcount = function(key, min, max, callback) {
        var self = this;
        var promise = new Promise(function(len) {
            self.emit(len);
        });
        callback = callback || utils.noop;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    var err = new Error('no such key');

                    promise.reject(err);
                    callback(err);
                }
            })
            .then(function(data) {
                var hashs = Object
                    .keys(data.shm)
                    .filter(function(score) {
                        return (min <= score && score <= max);
                    })
                    .map(function(score) {
                        return data.shm[score];
                    });

                var len = 0;

                hashs.forEach(function(hash) {
                    len += hash.length;
                });

                promise.resolve(len);
                callback(null, len);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    min.zrem = function(key) {
        var self = this;
        var promise = new Promise(function(len) {
            self.emit('zrem', len);
        });
        var callback = arguments[arguments.length - 1];

        var members = [].slice.call(arguments, 1);

        if ('function' !== typeof callback) {
            callback = utils.noop;
        } else {
            members.pop();
        }

        var removeds = 0;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    var err = new Error('no such key');

                    promise.reject(err);
                    callback(err);
                }
            })
            .then(function(data) {
                var n = members.length;

                var p = new Promise();

                for (var i = 0; i < members.length; i++) {
                    (function(index) {
                        var hash = data.ms.indexOf(members[index]);

                        if (hash >= 0) {
                            data.ms[hash] = 0;
                            var score = data.hsm[hash]
                            delete data.hsm[hash];

                            var ii = data.shm[score].indexOf(hash);
                            if (ii >= 0) {
                                data.shm[score].splice(ii, 1);
                            }

                            removeds++;

                            --n || p.resolve(data);
                        } else {
                            n--;
                        }
                    })(i);
                }

                return p;
            })
            .then(function(data) {
                return self.set(key, data);
            })
            .then(function() {
                promise.resolve(removeds);
                callback(null, removeds);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(null, err);
            });

        return promise;
    };

    min.zscore = function(key, member, callback) {
        var self = this;
        var promise = new Promise();
        callback = callback || utils.noop;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    var err = new Error('no such key');

                    promise.reject(err);
                    callback(err);
                }
            })
            .then(function(data) {
                var hash = data.ms.indexOf(member);

                if (hash >= 0) {
                    var score = data.hsm[hash];

                    promise.resolve(score);
                    callback(null, score);
                } else {
                    var err = new Error('This member does not be in the set');

                    promise.reject(err);
                    callback(err);
                }
            })

        return promise;
    };

    min.zrange = function(key, min, max, callback) {
        var self = this;
        var promise = new Promise(function(len) {
            self.emit(len);
        });
        callback = callback || utils.noop;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    var err = new Error('no such key');

                    promise.reject(err);
                    callback(err);
                }
            })
            .then(function(data) {
                var hashs = Object
                    .keys(data.shm)
                    .filter(function(score) {
                        return (min <= score && score <= max);
                    })
                    .map(function(score) {
                        return data.shm[score];
                    });

                var members = [];

                hashs.forEach(function(hash) {
                    members = members.concat(hash.map(function(row) {
                        return data.ms[row];
                    }));;
                });

                promise.resolve(members);
                callback(null, members);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        promise.withScore = function(callback) {
            var p = new Promise();
            callback = callback || utils.noop;

            promise
                .then(function(members) {
                    var multi = self.multi();

                    members.forEach(function(member) {
                        multi.zscore(key, member);
                    });

                    multi.exec(function(err, replies) {
                        if (err) {
                            callback(err);
                            return p.reject(err);
                        }

                        var rtn = [];

                        replies.forEach(function(reply, ii) {
                            rtn.push({
                                member: members[ii],
                                score: reply[0]
                            });
                        });

                        p.resolve(rtn);
                        callback(null, rtn);
                    });
                })

            return p;
        };

        return promise;
    };

    min.zrevrange = function(key, min, max, callback) {
        var self = this;
        var promise = new Promise(function(len) {
            self.emit(len);
        });
        callback = callback || utils.noop;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    var err = new Error('no such key');

                    promise.reject(err);
                    callback(err);
                }
            })
            .then(function(data) {
                var hashs = Object
                    .keys(data.shm)
                    .reverse()
                    .filter(function(score) {
                        return (min <= score && score <= max);
                    })
                    .map(function(score) {
                        return data.shm[score];
                    });

                var members = [];

                hashs.forEach(function(hash) {
                    members = members.concat(hash.map(function(row) {
                        return data.ms[row];
                    }));;
                });

                promise.resolve(members);
                callback(null, members);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        promise.withScore = function(callback) {
            var p = new Promise();
            callback = callback || utils.noop;

            promise
                .then(function(members) {
                    var multi = self.multi();

                    members.forEach(function(member) {
                        multi.zscore(key, member);
                    });

                    multi.exec(function(err, replies) {
                        if (err) {
                            callback(err);
                            return p.reject(err);
                        }

                        var rtn = [];

                        replies.forEach(function(reply, ii) {
                            rtn.push({
                                member: members[ii],
                                score: reply[0]
                            });
                        });

                        p.resolve(rtn);
                        callback(null, rtn);
                    });
                })

            return p;
        };

        return promise;
    };

    min.zincrby = function(key, increment, member, callback) {
        var self = this;
        var promise = new Promise(function(score) {
            self.emit('zincrby', key, increment, member, score);
        });
        callback = callback || utils.noop;

        var newScore = null;

        self.exists(key)

            .then(function(exists) {
                if (exists) {
                    return self.zscore(key, member);
                } else {
                    self.zadd(key, increment, member, callback)
                        .then(promise.resolve.bind(promise))
                        .fail(promise.reject.bind(promise));
                }
            })
            .then(function(score) {
                return self.get(key);
            })
            .then(function(data) {
                var hash = data.ms.indexOf(member);
                var score = data.hsm[hash];

                newScore = score + increment;

                var ii = data.shm[score].indexOf(hash);
                data.shm[score].splice(ii, 1);

                data.hsm[hash] = newScore;
                if (data.shm[newScore]) {
                    data.shm[newScore].push(hash);
                } else {
                    data.shm[newScore] = [ hash ];
                }

                return self.set(key, data);
            })
            .then(function() {
                promise.resolve(newScore);
                callback(null, newScore);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    min.zdecrby = function(key, decrement, member, callback) {
        var self = this;
        var promise = new Promise(function(score) {
            self.emit('zdecrby', key, decrement, member, score);
        });
        callback = callback || utils.noop;

        var newScore = null;

        self.exists(key)

            .then(function(exists) {
                if (exists) {
                    return self.zscore(key, member);
                } else {
                    var err = new Error('no such key');

                    promise.reject(err);
                    return callback(err);
                }
            })
            .then(function(score) {
                return self.get(key);
            })
            .then(function(data) {
                var hash = data.ms.indexOf(member);
                var score = data.hsm[hash];

                newScore = score - decrement;

                var ii = data.shm[score].indexOf(hash);
                data.shm[score].splice(ii, 1);

                data.hsm[hash] = newScore;
                if (data.shm[newScore]) {
                    data.shm[newScore].push(hash);
                } else {
                    data.shm[newScore] = [ hash ];
                }

                return self.set(key, data);
            })
            .then(function() {
                promise.resolve(newScore);
                callback(null, newScore);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    min.zrank = function(key, member, callback) {
        var self = this;
        var promise = new Promise();
        callback = callback || utils.noop;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    var err = new Error('no such key');

                    promise.reject(err);
                    return callback(err);
                }
            })
            .then(function(data) {
                var scores = Object.keys(data.shm);
                var score = data.hsm[data.ms.indexOf(member)];

                var rank = scores.indexOf(score);

                promise.resolve(rank);
                callback(null, rank);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    min.zrevrank = function(key, member, callback) {
        var self = this;
        var promise = new Promise();
        callback = callback || utils.noop;

        self.exists(key)
            .then(function(exists) {
                if (exists) {
                    return self.get(key);
                } else {
                    var err = new Error('no such key');

                    promise.reject(err);
                    return callback(err);
                }
            })
            .then(function(data) {
                var scores = Object.keys(data.shm);
                var score = data.hsm[data.ms.indexOf(member)];

                var rank = scores.reverse().indexOf(score);

                promise.resolve(rank);
                callback(null, rank);
            })
            .fail(function(err) {
                promise.reject(err);
                callback(err);
            });

        return promise;
    };

    function Multi(_nano) {
        var self = this;
        this.queue = [];
        this.last = null;
        this.state = 0;
        this.min = _nano;

        for (var prop in _nano) {
            if (_nano.hasOwnProperty(prop) && 'function' === typeof _nano[prop]) {
                (function(method) {
                    self[method] = function() {
                        self.queue.push({
                            method: method,
                            args: arguments
                        });

                        return self;
                    };
                })(prop);
            }
        }
    }
    Multi.prototype.exec = function(callback) {
        var self = this;
        var results = [];

        (function loop(task) {
            if (task) {
                self.min[task.method].apply(self.min, task.args)
                    .then(function() {
                        results.push(arguments);
                        loop(self.queue.shift());
                    })
                    .fail(function(err) {
                        callback(err, results);
                    });
            } else {
                callback(null, results);
            }
        })(self.queue.shift());
    };

    min.multi = function() {
        return new Multi(this);
    };

    function Sorter(key, _min, callback) {
        var self = this;
        self.min = _min;
        self.callback = callback;
        self.result = [];
        self.keys = {};
        self.promise = new Promise();
        self.sortFn = function(a, b) {
            if (utils.isNumber(a) && utils.isNumber(b)) {
                return a - b;
            } else {
                return JSON.stringify(a) > JSON.stringify(b);
            }
        };

        var run = function() {
            self.min.exists(key)
                .then(function(exists) {
                    if (exists) {
                        return self.min.get(key);
                    } else {
                        return new Error('no such key');
                    }
                })
                .then(function(value) {
                    var p = new Promise();

                    switch (true) {
                        case Array.isArray(value):
                            p.resolve(value);
                            break;
                        case (value.ms && Array.isArray(value.ms)):
                            p.resolve(value.ms);
                            break;

                        default:
                            return new Error('content type wrong');
                    }

                    return p;
                })
                .then(function(data) {
                    self.result = data.sort(self.sortFn);

                    self.result.forEach(function(chunk) {
                        self.keys[chunk] = chunk;
                    });

                    self.promise.resolve(self.result);
                    self.callback(null, self.result);
                })
                .fail(function(err) {
                    self.promise.reject(err);
                    self.callback(err);
                });
        };

        // Promise Shim
        (function loop(methods) {
            var curr = methods.shift();

            if (curr) {
                self[curr] = function() {
                    return self.promise[curr].apply(self.promise, arguments);
                };

                loop(methods);
            } else {
                run();
            }
        })([ 'then', 'fail', 'done']);
    }
    Sorter.prototype.by = function(pattern, callback) {
        var self = this;
        callback = callback || utils.noop;

        var src2ref = {};
        var refs = {};
        var aviKeys = [];

        // TODO: Sort by hash field
        var field = null;

        if (pattern.indexOf('->') > 0) {
            var i = pattern.indexOf('->');
            field = pattern.substr(i + 2);
            pattern = pattern.substr(0, pattern.length - i);
        }
        var isHash = !!field;

        self.min.keys(pattern)
            .then(function(keys) {
                var filter = new RegExp(pattern
                    .replace('?', '(.)')
                    .replace('*', '(.*)'));

                for (var i = 0; i < keys.length; i++) {
                    var symbol = filter.exec(keys[i])[1];

                    if (self.result.indexOf(symbol) >= 0) {
                        src2ref[keys[i]] = symbol;
                    }
                }

                aviKeys = Object.keys(src2ref);

                return self.min.mget(aviKeys.slice());
            })
            .then(function(values) {
                var reverse = {};

                for (var i = 0; i < values.length; i++) {
                    reverse[JSON.stringify(values[i])] = aviKeys[i];
                }

                values.sort(self.sortFn);

                var newResult = values
                    .map(function(value) {
                        return reverse[JSON.stringify(value)];
                    })
                    .map(function(key) {
                        return src2ref[key];
                    });

                self.result = newResult;

                self.promise.resolve(newResult);
                callback(null, newResult);
            })
            .fail(function(err) {
                self.promise.reject(err);
                callback(err);
                self.callback(err);
            });

        return this;
    };
    Sorter.prototype.asc = function(callback) {
        var self = this;
        callback = callback || utils.noop;

        self.sortFn = function(a, b) {
            if (utils.isNumber(a) && utils.isNumber(b)) {
                return a - b;
            } else {
                return JSON.stringify(a) > JSON.stringify(b);
            }
        };

        var handle = function(result) {
            self.result = result.sort(self.sortFn);

            self.promise.resolve(self.result);
            callback(null, self.result);
        };

        if (self.promise.ended) {
            handle(self.result);
        } else {
            self.promise.once('resolve', handle);
        }

        return self;
    };
    Sorter.prototype.desc = function(callback) {
        var self = this;
        callback = callback || utils.noop;

        self.sortFn = function(a, b) {
            if (utils.isNumber(a) && utils.isNumber(b)) {
                return b - a;
            } else {
                return JSON.stringify(a) < JSON.stringify(b);
            }
        };

        var handle = function(result) {
            self.result = result.sort(self.sortFn);

            self.promise.resolve(self.result);
            callback(null, self.result);
        };

        if (self.promise.ended) {
            handle(self.result);
        } else {
            self.promise.once('resolve', handle);
        }

        return self;
    };
    Sorter.prototype.get = function(pattern, callback) {
        var self = this;
        callback = callback || utils.noop;

        var handle = function(_result) {
            var result = [];

            (function loop(res) {
                var curr = res.shift();

                if (!utils.isUndefined(curr)) {
                    if (Array.isArray(curr)) {
                        var key = self.keys[curr[0]];

                        self.min.get(pattern.replace('*', key))
                            .then(function(value) {
                                curr.push(value);
                                result.push(curr);

                                loop(res);
                            })
                            .fail(function(err) {
                                self.promise.reject(err);
                                callback(err);
                            });

                    } else if (curr.substr || utils.isNumber(curr)) {
                        var key = self.keys[curr];

                        self.min.get(pattern.replace('*', key))
                            .then(function(value) {
                                result.push([ value ]);
                                if (value.substr || utils.isNumber(value)) {
                                    self.keys[value] = key;
                                } else {
                                    self.keys[JSON.stringify(value)] = key;
                                }

                                loop(res);
                            })
                            .fail(function(err) {
                                self.promise.reject(err);
                                callback(err);
                            });
                    }
                } else {
                    self.result = result;

                    self.promise.resolve(result);
                    callback(null, result);
                }
            })(_result.slice());
        };

        if (self.promise.ended) {
            handle(self.result);
        } else {
            self.promise.once('resolve', handle);
        }

        return this;
    };
    Sorter.prototype.hget = function(pattern, field, callback) {
        callback = callback || utils.noop;
        var self = this;

        var handle = function(_result) {
            var result = [];

            (function loop(res) {
                var curr = res.shift();

                if (!utils.isUndefined(curr)) {
                    if (Array.isArray(curr)) {
                        var key = self.keys[curr[0]];

                        self.min.hget(pattern.replace('*', key), field)
                            .then(function(value) {
                                curr.push(value);
                                result.push(curr);

                                loop(res);
                            })
                            .fail(function(err) {
                                self.promise.reject(err);
                                callback(err);
                            });

                    } else if (curr.substr || utils.isNumber(curr)) {
                        var key = self.keys[curr];

                        self.min.hget(pattern.replace('*', key))
                            .then(function(value) {
                                result.push([ value ]);
                                if (value.substr || utils.isNumber(value)) {
                                    self.keys[value] = key;
                                } else {
                                    self.keys[JSON.stringify(value)] = key;
                                }

                                loop(res);
                            })
                            .fail(function(err) {
                                self.promise.reject(err);
                                callback(err);
                            });
                    }
                } else {
                    self.result = result;

                    self.promise.resolve(result);
                    callback(null, result);
                }
            })(_result.slice());
        };

        if (self.promise.ended) {
            handle(self.result);
        } else {
            self.promise.once('resolve', handle);
        }

        return this;
    };
    Sorter.prototype.limit = function(offset, count, callback) {
        callback = callback || utils.noop;
        var self = this;

        var handle = function(result) {
            self.result = result.splice(offset, count);

            self.promise.resolve(self.result);
            callback(null, self.result);
        };

        if (self.promise.ended) {
            handle(self.result);
        } else {
            self.promise.once('resolve', handle);
        }

        return this;
    };
    Sorter.prototype.flatten = function(callback) {
        callback = callback || utils.noop;
        var self = this;

        if (self.promise.ended) {
            var rtn = [];

            for (var i = 0; i < self.result.length; i++) {
                for (var j = 0; j < self.result[i].length; j++) {
                    rtn.push(self.result[i][j]);
                }
            }

            self.result = rtn;

            self.promise.resolve(rtn);
            callback(null, rtn);
        } else {
            self.promise.once('resolve', function(result) {
                var rtn = [];

                for (var i = 0; i < result.length; i++) {
                    for (var j = 0; j < result[i].length; j++) {
                        rtn.push(result[i][j]);
                    }
                }

                self.result = rtn;

                self.promise.resolve(rtn);
                callback(null, rtn);
            });
        }

        return this;
    };
    Sorter.prototype.store = function(dest, callback) {
        var self = this;
        callback = callback || utils.noop;

        if (self.promise.ended) {
            self.min.set(dest, self.result)
                .then(function() {
                    self.promise.resolve(self.result);
                    callback(null, self.result);
                })
                .fail(function(err) {
                    self.promise.reject(err);
                    callback(err);
                });
        } else {
            self.promise.once('resolve', function(result) {
                self.min.set(dest, result)
                    .then(function() {
                        self.promise.resolve(result);
                        callback(null, result);
                    })
                    .fail(function(err) {
                        self.promise.reject(err);
                        callback(err);
                    });
            });
        }

        return this;
    };

    min.sort = function(key, callback) {
        callback = callback || utils.noop;

        return new Sorter(key, this, callback);
    };

    function Scanner(cursor, pattern, count, min) {
        pattern = pattern || '*';

        this.cursor = cursor || 0;
        this.pattern = new RegExp(pattern.replace('*', '(.*)'));
        this.limit = count > -1 ? count : 10;
        this.end = this.cursor;

        this.parent = min;
    }
    Scanner.prototype.scan = function(callback) {
        var self = this;

        var rtn = [];

        self.parent.get('min_keys')
            .then(function(data) {
                data = JSON.parse(data);

                var keys = Object.keys(data);

                (function scan(ii) {
                    var key = keys[ii];

                    if (key && self.pattern.test(key) && key !== 'min_keys') {
                        rtn.push(key);

                        if ((++self.end - self.cursor) >= self.limit) {
                            return callback(null, rtn, self.end);
                        }
                    } else if (!key) {
                        self.end = 0;
                        return callback(null, rtn, self.end);
                    }

                    return scan(++ii);
                })(self.cursor);
            }, function(err) {
                callback(err);
            });

        return this;
    };
    Scanner.prototype.match = function(pattern, callback) {
        this.pattern = new RegExp(pattern.replace('*', '(.*)'));
        this.end = this.cursor;

        return this.scan(callback || utils.noop);
    };
    Scanner.prototype.count = function(count, callback) {
        this.limit = count;
        this.end = this.cursor;

        return this.scan(callback || utils.noop);
    };

    min.scan = function(cursor, callback) {
        var self = this;
        callback = callback || utils.noop;

        var scanner = new Scanner(cursor, null, -1, self);

        scanner.scan(callback);

        return scanner;
    };

    // Apply
    min.exists('min_keys')
        .then(function(exists) {
            if (exists) {
                return min.get('min_keys');
            }
        })
        .then(function(keys) {
            _keys = JSON.parse(keys);
        });

    return min;
});